﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// Reviewed

#pragma once
#include "GbmpDefines.h"
#include "GcmpMathInterface.h"

#include "GbmpConstants.h"
#include "GbmpTypeDefs.h"
#include "DebugMessage.h"
#include <cmath>
#include <algorithm>


namespace gcmp
{
    /// \brief  区间的定义
    ///
    /// 
    class GCMP_MATH_INTERFACE_EXPORT Intervald
    {
    public:
        DEFINE_VISUAL_TYPE_DOUBLE()

    public:

        /// \brief  默认构造函数
        /// 
        explicit Intervald();

        /// \brief   构造函数
        ///
        /// \param min  最小值
        /// \param max  最大值
        /// 
        explicit Intervald(double min, double max);

        /// \brief  拷贝构造函数
        ///
        /// \param src 输入区间
        /// 
        Intervald(const Intervald & src);

    public:

        /// \brief   创建空的区间
        ///
        /// \return  空的区间
        static Intervald CreateEmptyIntervald();

        /// \brief  根据最小值和最大值构建区间
        ///
        /// \param min  最小值
        /// \param max  最大值
        /// \return   区间
        static Intervald Create(double min, double max);

        /// \brief  使区间无穷大
        ///
        /// \return 
        bool MakeInfinite();

        /// \brief  获取最小值
        ///
        /// \return  最小值
        double GetMin() const;

        /// \brief  获取最大值
        ///
        /// \return  最大值
        double GetMax() const;

        /// \brief   设置区间
        ///
        /// \param min 最小值
        /// \param max 最大值
        /// \return  true: 成功，false: 失败
        bool Set(double min, double max);

        /// \brief  初始化区间成空区间
        ///
        /// \return  true: 成功，false: 失败
        bool MakeEmpty();

        /// \brief  判断区间是否为空
        ///
        /// \return  true区间为空，false不空
        bool IsEmpty() const;

        /// \brief  获取区间长度，上下限差的绝对值
        ///
        /// \return  区间长度
        double Length() const;

        /// \brief   获取区间中值
        ///
        /// \return  区间中值
        double Middle() const;

        /// \brief   两个区间的交集
        ///
        /// \param other 另一个区间
        /// \return  交集
        Intervald Intersection(const Intervald & other) const;

        /// \brief   带容差的判断两个非空区间是否相交
        ///
        /// \param other  另一个区间
        /// \param epsilon 容差
        /// \return  true: 相交， false, 不相交
        bool IsIntersect(const Intervald & other, double epsilon = gcmp::Constants::DOUBLE_EPS) const;

        /// \brief  带容差的判断两个区间是否相同
        ///
        /// \param other  另一个区间
        /// \param epsilon 容差
        /// \return true: 相等， false, 不相等
        bool IsEqual(const Intervald  & other, double epsilon = gcmp::Constants::DOUBLE_EPS) const;

        /// \brief   带容差的判断值是否在区间内
        ///
        /// \param a  任意值
        /// \param epsilon 容差
        /// \return  true: 包含， false, 不包含
        bool Contain(double a, double epsilon = gcmp::Constants::DOUBLE_EPS) const;

        /// \brief   若数值在区间外, 将其拉回到临近的区间端点
        ///
        /// \param value  数值
        /// \return  true  数值被拉回到区间端点;  false  数值在区间内, 未被拉回
        bool Clamp(double &value) const;

        /// \brief   区间集合减法，如果两个结果区间都不空，说明this区间包含other区间
        ///
        /// \param other  减区间
        /// \param sub1  第一个结果区间
        /// \param sub2  第二个结果区间
        /// \return true: 成功， false, 不成功
        bool Substract(const Intervald & other, Intervald & sub1, Intervald & sub2) const;

        /// \brief   合并区间，如果给定值a恰好在原来区间内，区间维持不变，如果不在，将区间扩充至恰好包含给定值a
        ///
        /// \param a  给定数值
        /// \return true: 成功， false, 不成功
        bool Merge(double a);

        /// \brief   区间对原点的镜像
        ///
        void Mirror();
    public:

        /// \brief  带容差的判断区间是否在区间内
        ///
        /// \param other  另一个区间
        /// \param epsilon 容差
        /// \return true: 包含， false, 不包含
        bool Contain(const Intervald& other, double epsilon = gcmp::Constants::DOUBLE_EPS) const;

        /// \brief  合并区间
        ///
        /// \param other 另一个区间
        /// \return 
        bool Merge(const Intervald & other);

        /// \brief  区间比例变换，上下限都乘以指定因子
        ///
        /// \param factor      指定因子
        /// \return  不改变原区间，返回一个新区间
        Intervald operator*(double factor) const;

        /// \brief  增加区间的最小和最大值
        ///
        /// \param value     增加值
        /// \return  区间
        Intervald operator+(double value) const;

        /// \brief  减小区间的最小和最大值
        ///
        /// \param value     减小值
        /// \return  区间
        Intervald operator-(double value) const;

        /// \brief  区间的最小和最大值除以某个值
        ///
        /// \param value    某个值
        /// \return  区间
        Intervald operator/(double value) const;

        /// \brief  区间和另一个区间的最小和最大值分别相加
        ///
        /// \param value    另一个区间
        /// \return  区间
        Intervald operator+(const Intervald& other) const;

        /// \brief  区间和另一个区间的最小和最大值分别相减
        ///
        /// \param value    另一个区间
        /// \return  区间
        Intervald operator-(const Intervald& other) const;

        /// \brief  区间比例变换，this区间上下限都乘以指定因子
        ///
        /// \param  factor      指定因子
        /// \return  返回原区间的引用
        Intervald & operator*=(double factor);

    private:
        double m_min, m_max;
    };

    /// \brief  区间比例变换，上下限都乘以指定因子
    ///
    /// \param factor      指定因子
    /// \return  不改变原区间，返回一个新区间
    GCMP_MATH_INTERFACE_EXPORT Intervald operator*(double factor, const Intervald& i);

    /// \brief  判断区间是否相等
    ///
    /// \param v1    区间
    /// \param v2    另一区间
    /// \return  true: 相等   false: 不等
    GCMP_MATH_INTERFACE_EXPORT bool operator ==(const Intervald & v1, const Intervald & v2);

    /// \brief  判断区间是否不等
    ///
    /// \param v1    区间
    /// \param v2    另一区间
    /// \return  true: 不等   false: 相等
    GCMP_MATH_INTERFACE_EXPORT bool operator !=(const Intervald & v1, const Intervald & v2);


} // namespace


